use std::collections::HashMap;
use crate::error::{Result, VerticaError};

/// Error severity levels following PostgreSQL/Vertica conventions
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorSeverity {
    /// 常规错误，可恢复的错误状态
    Error,
    /// 致命错误，会导致当前会话终止
    Fatal,
    /// 紧急错误，会导致服务器进程终止
    Panic,
    /// 警告信息，提示可能存在问题但不会中断操作
    Warning,
    /// 通知信息，提供额外的信息但不表示存在问题
    Notice,
    /// 调试信息，用于开发和调试阶段
    Debug,
    /// 一般信息，提供操作的相关信息
    Info,
    /// 日志信息，用于记录系统操作和状态
    Log,
}
impl std::fmt::Display for ErrorSeverity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ErrorSeverity::Error => write!(f, "ERROR"),
            ErrorSeverity::Fatal => write!(f, "FATAL"),
            ErrorSeverity::Panic => write!(f, "PANIC"),
            ErrorSeverity::Warning => write!(f, "WARNING"),
            ErrorSeverity::Notice => write!(f, "NOTICE"),
            ErrorSeverity::Debug => write!(f, "DEBUG"),
            ErrorSeverity::Info => write!(f, "INFO"),
            ErrorSeverity::Log => write!(f, "LOG"),
        }
    }
}
/// Structured error response following vertica-sql-go patterns
#[derive(Debug, Clone, Default)]
pub struct ErrorResponse {
    /// 错误严重级别，遵循 PostgreSQL/Vertica 约定
    pub severity: Option<ErrorSeverity>,
    /// SQL 状态码，标识错误类型
    pub sql_state: Option<String>,
    /// 错误消息，简要描述错误内容
    pub message: Option<String>,
    /// 错误详情，提供更详细的错误信息
    pub detail: Option<String>,
    /// 错误提示，给出解决错误的建议
    pub hint: Option<String>,
    /// 错误发生的位置，通常是 SQL 语句中的位置
    pub position: Option<String>,
    /// 内部错误位置，供数据库内部使用
    pub internal_position: Option<String>,
    /// 内部查询语句，可能是引发错误的内部 SQL
    pub internal_query: Option<String>,
    /// 错误发生的上下文位置，通常是 SQL 语句中的位置描述
    pub where_clause: Option<String>,
    /// 错误发生的文件，通常是数据库内部文件
    pub file: Option<String>,
    /// 错误发生的行号，通常是数据库内部文件的行号
    pub line: Option<String>,
    /// 错误发生的例程名，通常是数据库内部函数名
    pub routine: Option<String>,
    /// 错误发生的列名
    pub column: Option<String>,
    /// 错误涉及的数据类型
    pub datatype: Option<String>,
    /// 错误涉及的约束名
    pub constraint: Option<String>,
    /// 错误涉及的模式名
    pub schema: Option<String>,
    /// 错误涉及的表名
    pub table: Option<String>,
}
impl ErrorResponse {
    /// Parse error response from server message
    pub fn parse_from_message(data: &[u8]) -> Result<Self> {
        let mut error = ErrorResponse::default();
        let mut fields = HashMap::new();
        
        let mut pos = 0;
        while pos < data.len() {
            if pos >= data.len() {
                break;
            }
            
            let field_type = data[pos];
            pos += 1;
            
            if field_type == 0 {
                break; // End of fields
            }
            
            let null_pos = data[pos..]
                .iter()
                .position(|&b| b == 0)
                .ok_or_else(|| VerticaError::Connection("Invalid error response format".to_string()))?;
            
            let value = String::from_utf8_lossy(&data[pos..pos + null_pos]).to_string();
            pos += null_pos + 1;
            
            fields.insert(field_type as char, value);
        }
        
        // Map PostgreSQL/Vertica error field types
        if let Some(severity) = fields.get(&'S') {
            error.severity = Some(match severity.as_str() {
                "ERROR" => ErrorSeverity::Error,
                "FATAL" => ErrorSeverity::Fatal,
                "PANIC" => ErrorSeverity::Panic,
                "WARNING" => ErrorSeverity::Warning,
                "NOTICE" => ErrorSeverity::Notice,
                "DEBUG" => ErrorSeverity::Debug,
                "INFO" => ErrorSeverity::Info,
                "LOG" => ErrorSeverity::Log,
                _ => ErrorSeverity::Error,
            });
        }
        
        if let Some(severity) = fields.get(&'V') {
            error.severity = Some(match severity.as_str() {
                "ERROR" => ErrorSeverity::Error,
                "FATAL" => ErrorSeverity::Fatal,
                "PANIC" => ErrorSeverity::Panic,
                "WARNING" => ErrorSeverity::Warning,
                "NOTICE" => ErrorSeverity::Notice,
                "DEBUG" => ErrorSeverity::Debug,
                "INFO" => ErrorSeverity::Info,
                "LOG" => ErrorSeverity::Log,
                _ => ErrorSeverity::Error,
            });
        }
        
        error.sql_state = fields.get(&'C').cloned();
        error.message = fields.get(&'M').cloned();
        error.detail = fields.get(&'D').cloned();
        error.hint = fields.get(&'H').cloned();
        error.position = fields.get(&'P').cloned();
        error.internal_position = fields.get(&'p').cloned();
        error.internal_query = fields.get(&'q').cloned();
        error.where_clause = fields.get(&'W').cloned();
        error.file = fields.get(&'F').cloned();
        error.line = fields.get(&'L').cloned();
        error.routine = fields.get(&'R').cloned();
        error.column = fields.get(&'c').cloned();
        error.datatype = fields.get(&'d').cloned();
        error.constraint = fields.get(&'n').cloned();
        error.schema = fields.get(&'s').cloned();
        error.table = fields.get(&'t').cloned();
        
        Ok(error)
    }
    
    /// Convert to VerticaError
    pub fn to_error(&self) -> VerticaError {
        let message = self.message.as_deref().unwrap_or("Unknown error");
        let detail = self.detail.as_deref().unwrap_or("");
        let hint = self.hint.as_deref().unwrap_or("");
        
        let full_message = if !detail.is_empty() || !hint.is_empty() {
            format!("{} {} {}", message, detail, hint).trim().to_string()
        } else {
            message.to_string()
        };
        
        match self.severity {
            Some(ErrorSeverity::Fatal) | Some(ErrorSeverity::Panic) => {
                VerticaError::Connection(full_message)
            }
            _ => VerticaError::Query(full_message),
        }
    }
    
    /// Get SQL state code
    pub fn sql_state(&self) -> Option<&str> {
        self.sql_state.as_deref()
    }
    
    /// Get error message
    pub fn message(&self) -> Option<&str> {
        self.message.as_deref()
    }
    
    /// Check if this is a specific SQL state
    pub fn is_sql_state(&self, state: &str) -> bool {
        self.sql_state.as_deref() == Some(state)
    }
}

impl std::fmt::Display for ErrorResponse {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(severity) = &self.severity {
            write!(f, "{}", severity)?;
        }
        
        if let Some(sql_state) = &self.sql_state {
            write!(f, " [{}]", sql_state)?;
        }
        
        if let Some(message) = &self.message {
            write!(f, ": {}", message)?;
        }
        
        if let Some(detail) = &self.detail {
            write!(f, "\nDetail: {}", detail)?;
        }
        
        if let Some(hint) = &self.hint {
            write!(f, "\nHint: {}", hint)?;
        }
        
        if let Some(position) = &self.position {
            write!(f, "\nPosition: {}", position)?;
        }
        
        if let Some(where_clause) = &self.where_clause {
            write!(f, "\nWhere: {}", where_clause)?;
        }
        
        Ok(())
    }
}

impl std::error::Error for ErrorResponse {}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_parse_error_response() {
        // Create a sample error response
        let mut data = vec![];
        data.push(b'S'); // Severity
        data.extend_from_slice(b"ERROR\0");
        data.push(b'C'); // SQL State
        data.extend_from_slice(b"42P01\0");
        data.push(b'M'); // Message
        data.extend_from_slice(b"relation \"users\" does not exist\0");
        data.push(b'D'); // Detail
        data.extend_from_slice(b"Referenced table not found\0");
        data.push(0); // End marker
        
        let error = ErrorResponse::parse_from_message(&data).unwrap();
        
        assert_eq!(error.severity, Some(ErrorSeverity::Error));
        assert_eq!(error.sql_state(), Some("42P01"));
        assert_eq!(error.message(), Some("relation \"users\" does not exist"));
        assert_eq!(error.detail, Some("Referenced table not found".to_string()));
    }
    
    #[test]
    fn test_parse_empty_error_response() {
        let error = ErrorResponse::parse_from_message(&[0]).unwrap();
        assert!(error.message.is_none());
        assert!(error.sql_state.is_none());
    }
}